﻿using System;
using System.Collections.Generic;
using RayDen.Library.Core;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.Data;

namespace RayDen.RayEngine.Samplers
{
    public class UniformGridSampler : ISampler
    {
        public static int PreviewPasses = 3;
        public static int HighQualityPass = PreviewPasses + 1;
        public static int IterationPerPass = 64;
        public static float GridSize = 1.0f;



        public int Width { get { return screenWidth; } protected set { } }
        public int Height { get { return screenHeight; } protected set { } }

        public int screenWidth, screenHeight, screenStartLine;
        int currentSubSampleIndex, currentSubSampleComponentIndex;
        public int pass;
        public bool lowLatency;
        public int valuesPerSample;

        private FastRandom rnd;

        public int tIdx, wc;

        private List<Tuple<int, int, int>> samples;



        public UniformGridSampler()
        {
            rnd = new FastRandom();
            samples = new List<Tuple<int, int, int>>();
            TotalSamples = 0L;
        }

        public void Init(int width, int height)
        {
            rnd.Reinitialise((int)DateTime.Now.Ticks);
            if (screenWidth == 0 && screenHeight == 0)
            {
                Width = screenWidth = width;
                Height = screenHeight = height;
                screenWidth = width - 1;
                screenHeight = height - 1;
            }
            screenStartLine = Math.Max(0, screenStartLine - 1);
            currentSubSampleIndex = 0;
            pass = 0;
            TotalSamples = 0;
            for (int j = screenStartLine; j < (screenStartLine + Height); j ++)
            {
                for (int i = 0; i < (Width); i ++)
                {
                    var yS = j;
                    //((tIdx ) % 2) == 0 ? (screenStartLine + Height )- j :j;
                    var xS = i;
                    //((tIdx ) % 2) == 0 ? (Width) -i : i;
                    samples.Add(new Tuple<int, int, int>(xS, yS, 0));
                }
            }

        }

        public Sample GetSample(Sample prevSample)
        {
            TotalSamples++;
            Sample sample = new Sample(this);
            int scrX, scrY;

            if (pass <= PreviewPasses)
            {
                sample.imageX = rnd.Next(0, Width - 1) + 0.5f;
                sample.imageY = rnd.Next(0, screenStartLine + Height - 1) + 0.5f;
                wc++;
                if (wc >= Width * Height)
                {
                    pass++;
                    wc = 0;
                }
                return sample;
            }

            if (pass >= HighQualityPass)
            {
                if (currentSubSampleIndex >= samples.Count)
                {
                    pass += IterationPerPass;
                    currentSubSampleIndex = 0;
                    currentSubSampleComponentIndex = 0;
                }

                var subSample = samples[currentSubSampleIndex];

                if (currentSubSampleComponentIndex < (IterationPerPass))
                {
                    currentSubSampleComponentIndex++;
                }
                else
                {
                    currentSubSampleIndex++;
                    currentSubSampleComponentIndex = 0;
                }


                sample.imageX = subSample.Item1 + (float)(rnd.NextDouble()*GridSize) - (GridSize / 2f);
                sample.imageY = subSample.Item2 + (float)(rnd.NextDouble()*GridSize) - (GridSize / 2f);
            }
            return sample;
        }


        #region ISampler Members


        public float GetLazyValue(Sample sample)
        {
            return rnd.NextFloat();
        }

        public float GetLDValue(Sample sample)
        {
            return rnd.NextFloat();

        }


        public int GetPass()
        {
            return pass;
        }

        public long TotalSamples { get; set; }
        public float GetLazyValue(Sample sample, int index)
        {
            return sample.sampleData[unchecked(index)];
        }

        #endregion

        public void Reinitialise(int seed)
        {
            this.rnd.Reinitialise(seed);
        }

        public float NextFloat()
        {
            return rnd.NextFloat();
        }
    }
}